package com.nbufe.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.nbufe.common.core.utils.StringUtils;
import com.nbufe.domain.ResponseResult;
import com.nbufe.domain.dto.target.*;
import com.nbufe.domain.entity.CarbonTarget;
import com.nbufe.domain.entity.FossilFuel;
import com.nbufe.domain.entity.Process;
import com.nbufe.domain.vo.PageVo;
import com.nbufe.domain.vo.target.*;
import com.nbufe.enums.StageEnum;
import com.nbufe.exception.SystemException;
import com.nbufe.mapper.*;
import com.nbufe.service.CarbonTargetService;
import com.nbufe.utils.AnalysisUtil;
import com.nbufe.utils.BeanCopyUtils;
import com.nbufe.utils.DateUtil;
import com.nbufe.utils.FormatUtil;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

/**
 * (CarbonTarget)表服务实现类
 *
 * @author luzhiheng
 * @since 2024-01-10 10:28:34
 */
@Service("carbonTargetService")
public class CarbonTargetServiceImpl extends ServiceImpl<CarbonTargetMapper, CarbonTarget> implements CarbonTargetService {

    @Resource
    private ProcessMapper processMapper;

    @Resource
    private FossilFuelMapper fossilFuelMapper;

    @Resource
    private IndustoryProduceMapper industoryProduceMapper;

    @Resource
    private ElectricHeatMapper electricHeatMapper;

    @Resource
    private FixCarbonMapper fixCarbonMapper;

    @Override
    public void addTarget(AddTargetDto dto) {
        CarbonTarget carbonTarget = new CarbonTarget(dto);
        this.save(carbonTarget);
    }

    @Override
    public ResponseResult queryTargetPage(QueryTargetPageDto dto) {
        Page<CarbonTarget> page = new Page<>(dto.getPageNum(), dto.getPageSize());
        LambdaQueryWrapper<CarbonTarget> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(StringUtils.isNotEmpty(dto.getName()), CarbonTarget::getName, dto.getName());
        queryWrapper.eq(dto.getEnable() != null, CarbonTarget::getEnable, dto.getEnable());
        this.page(page, queryWrapper);

        PageVo pageVo = new PageVo(page.getTotal(), page.getRecords());
        return ResponseResult.okResult(pageVo);
    }

    @Override
    public ResponseResult enableTarget(Long id) {
        CarbonTarget carbonTarget = getCarbonTargetAndIsExist(id);
        carbonTarget.setEnable(1);
        this.updateById(carbonTarget);
        return ResponseResult.okResult();
    }

    private CarbonTarget getCarbonTargetAndIsExist(Long id) {
        CarbonTarget carbonTarget = this.getById(id);
        if (carbonTarget == null) {
            throw new SystemException("目标不存在");
        }
        return carbonTarget;
    }

    @Override
    public ResponseResult unEnableTarget(Long id) {
        CarbonTarget carbonTarget = getCarbonTargetAndIsExist(id);
        carbonTarget.setEnable(0);
        this.updateById(carbonTarget);
        return ResponseResult.okResult();
    }

    @Override
    public void editTarget(EditTargetDto dto) {
        CarbonTarget carbonTarget = getCarbonTargetAndIsExist(dto.getId());
        carbonTarget.setEditTarget(dto);
        this.updateById(carbonTarget);
    }

    @Override
    public ResponseResult queryStageProcessYearMonthEmission(QueryEmissionTargetDto dto) {
        List<Process> processList = processMapper.selectList(null);

        List<String> processes = dto.getProcess();
        List<String> stages = dto.getStages();

        List<Long> processIds = processList.stream()
                .filter(process -> processes.contains(process.getName()))
                .map(Process::getId)
                .collect(Collectors.toList());

        YearMonthProcessDto yearMonthProcessDto = new YearMonthProcessDto();
        yearMonthProcessDto.setBeginDate(DateUtil.removeZero(dto.getBeginDate()));
        yearMonthProcessDto.setEndDate(DateUtil.removeZero(dto.getEndDate()));
        yearMonthProcessDto.setProcessIds(processIds);

        List<TargetEmissionVo> fossiFuelEmissionVoList = new ArrayList<>();
        List<TargetEmissionVo> industoryEmissionVoList = new ArrayList<>();
        List<TargetEmissionVo> electricHeatEmissionVoList = new ArrayList<>();
        List<TargetEmissionVo> fixCarbonEmissionVoList = new ArrayList<>();

        List<StageEmissionVo> stageEmissionVoList = new ArrayList<>();
        for (String s : stages) {
            if (StageEnum.FOSSI_FUEL.getCode().equals(s)) {
                fossiFuelEmissionVoList = fossilFuelMapper.queryYearMonthProcessesRangeCO2(yearMonthProcessDto);
                stageEmissionVoList.add(new StageEmissionVo(StageEnum.FOSSI_FUEL.getValue(), fossiFuelEmissionVoList.stream().mapToDouble(TargetEmissionVo::getCarbonEmission).sum()));
            } else if (StageEnum.INDUSTRIAL_PRODUCTION.getCode().equals(s)) {
                industoryEmissionVoList = industoryProduceMapper.queryYearMonthProcessesRangeCO2(yearMonthProcessDto);
                stageEmissionVoList.add(new StageEmissionVo(StageEnum.INDUSTRIAL_PRODUCTION.getValue(), industoryEmissionVoList.stream().mapToDouble(TargetEmissionVo::getCarbonEmission).sum()));
            } else if (StageEnum.ELECTRIC_HEAT.getCode().equals(s)) {
                electricHeatEmissionVoList = electricHeatMapper.queryYearMonthProcessesRangeCO2(yearMonthProcessDto);
                stageEmissionVoList.add(new StageEmissionVo(StageEnum.ELECTRIC_HEAT.getValue(), electricHeatEmissionVoList.stream().mapToDouble(TargetEmissionVo::getCarbonEmission).sum()));
            } else if (StageEnum.CARBON_SEQUESTRATION_PRODUCT.getCode().equals(s)) {
                fixCarbonEmissionVoList = fixCarbonMapper.queryYearMonthProcessesRangeCO2(yearMonthProcessDto);
                stageEmissionVoList.add(new StageEmissionVo(StageEnum.CARBON_SEQUESTRATION_PRODUCT.getValue(), fixCarbonEmissionVoList.stream().mapToDouble(TargetEmissionVo::getCarbonEmission).sum()));
            }
        }
        YearMonthStageProcessVo vo = new YearMonthStageProcessVo();

        vo.setStageEmissionVoList(stageEmissionVoList);

        List<ProcessEmissionVo> processEmissionVoList = new ArrayList<>();
        for (Long processId : processIds) {
            Map<Long, List<TargetEmissionVo>> processFossiMap = fossiFuelEmissionVoList.stream().collect(Collectors.groupingBy(TargetEmissionVo::getProcessId));
            Map<Long, List<TargetEmissionVo>> processIndustoryMap = industoryEmissionVoList.stream().collect(Collectors.groupingBy(TargetEmissionVo::getProcessId));
            Map<Long, List<TargetEmissionVo>> processElectricHeatMap = electricHeatEmissionVoList.stream().collect(Collectors.groupingBy(TargetEmissionVo::getProcessId));
            Map<Long, List<TargetEmissionVo>> processFixCarbonMap = fixCarbonEmissionVoList.stream().collect(Collectors.groupingBy(TargetEmissionVo::getProcessId));

            List<TargetEmissionVo> emissionVoList = processFossiMap.get(processId) == null ? new ArrayList<>() : processFossiMap.get(processId);
            List<TargetEmissionVo> emissionVoList1 = processIndustoryMap.get(processId) == null ? new ArrayList<>() : processIndustoryMap.get(processId);
            List<TargetEmissionVo> emissionVoList2 = processElectricHeatMap.get(processId) == null ? new ArrayList<>() : processElectricHeatMap.get(processId);
            List<TargetEmissionVo> emissionVoList3 = processFixCarbonMap.get(processId) == null ? new ArrayList<>() : processFixCarbonMap.get(processId);
            double value = emissionVoList.stream().filter(e -> e.getCarbonEmission() != null).mapToDouble(TargetEmissionVo::getCarbonEmission).sum() +
                    emissionVoList1.stream().filter(e -> e.getCarbonEmission() != null).mapToDouble(TargetEmissionVo::getCarbonEmission).sum() +
                    emissionVoList2.stream().filter(e -> e.getCarbonEmission() != null).mapToDouble(TargetEmissionVo::getCarbonEmission).sum() +
                    emissionVoList3.stream().filter(e -> e.getCarbonEmission() != null).mapToDouble(TargetEmissionVo::getCarbonEmission).sum();
            processEmissionVoList.add(new ProcessEmissionVo(processId, value));
        }
        vo.setProcessEmissionVoList(processEmissionVoList);
        return ResponseResult.okResult(vo);
    }

    @Override
    public ResponseResult queryAllTarget() {
        LambdaQueryWrapper<CarbonTarget> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(CarbonTarget::getEnable, 1);
        List<CarbonTarget> list = this.list(queryWrapper);
        List<CarbonTargetVo> carbonTargetVoList = BeanCopyUtils.copyBeanList(list, CarbonTargetVo.class);
        for (CarbonTargetVo carbonTargetVo : carbonTargetVoList) {
            YearMonthStageProcessVo vo = (YearMonthStageProcessVo) queryStageProcessYearMonthEmission(new QueryEmissionTargetDto(carbonTargetVo)).getData();
            List<StageEmissionVo> stageEmissionVoList = vo.getStageEmissionVoList();
            double currentEmission = stageEmissionVoList.stream().filter(e -> e.getCarbonEmission() != null).mapToDouble(StageEmissionVo::getCarbonEmission).sum();
            carbonTargetVo.setCurrentEmission(FormatUtil.saveTwoDecimalPlace(currentEmission));
            Double targetAmount = carbonTargetVo.getTargetAmount();
            carbonTargetVo.setCurrentPercent(AnalysisUtil.calculateRate(currentEmission, targetAmount));
        }
        carbonTargetVoList = carbonTargetVoList.stream().sorted((o1, o2) -> o2.getCurrentPercent().compareTo(o1.getCurrentPercent())).collect(Collectors.toList());
        return ResponseResult.okResult(carbonTargetVoList);
    }

    @Override
    public ResponseResult queryWarningDetail(Long id) {
        CarbonTarget carbonTarget = this.getById(id);
        if (carbonTarget == null) {
            throw new SystemException("目标不存在");
        }
        YearMonthStageProcessVo vo = (YearMonthStageProcessVo) queryStageProcessYearMonthEmission(new QueryEmissionTargetDto(carbonTarget)).getData();
        List<StageEmissionVo> stageEmissionVoList = vo.getStageEmissionVoList().stream().sorted((o1, o2) -> o2.getCarbonEmission().compareTo(o1.getCarbonEmission())).collect(Collectors.toList());
        List<ProcessEmissionVo> processEmissionVoList = vo.getProcessEmissionVoList().stream().sorted((o1, o2) -> o2.getCarbonEmission().compareTo(o1.getCarbonEmission())).collect(Collectors.toList());
        processEmissionVoList = processEmissionVoList.stream().peek(e -> {
            Process process = processMapper.selectById(e.getType());
            e.setTypeName(process != null ? process.getName() : "");
        }).collect(Collectors.toList());
        Map<String, Object> map = new HashMap<>();
        map.put("stageEmissionVoList", stageEmissionVoList);
        map.put("processEmissionVoList", processEmissionVoList);
        return ResponseResult.okResult(map);
    }

    @Override
    public ResponseResult queryAllTargetByDate(Integer isUnderway) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");
        String formatDate = sdf.format(new Date());

        LambdaQueryWrapper<CarbonTarget> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(CarbonTarget::getEnable, 1);
        queryWrapper.orderByDesc(CarbonTarget::getCreateTime);
        List<CarbonTarget> list = this.list(queryWrapper);
        List<CarbonTargetVo> carbonTargetVoList = BeanCopyUtils.copyBeanList(list, CarbonTargetVo.class);
        if (0 == isUnderway) {
            // 已结束
            carbonTargetVoList = carbonTargetVoList.stream().filter(e -> {
                String endDate = e.getEndDate();
                return endDate.compareTo(formatDate) < 0;
            }).collect(Collectors.toList());
        } else if (1 == isUnderway) {
            // 进行中
            carbonTargetVoList = carbonTargetVoList.stream().filter(e -> {
                String endDate = e.getEndDate();
                return endDate.compareTo(formatDate) >= 0;
            }).collect(Collectors.toList());
        }

        for (CarbonTargetVo carbonTargetVo : carbonTargetVoList) {
            YearMonthStageProcessVo vo = (YearMonthStageProcessVo) queryStageProcessYearMonthEmission(new QueryEmissionTargetDto(carbonTargetVo)).getData();
            List<StageEmissionVo> stageEmissionVoList = vo.getStageEmissionVoList();
            double currentEmission = stageEmissionVoList.stream().filter(e -> e.getCarbonEmission() != null).mapToDouble(StageEmissionVo::getCarbonEmission).sum();
            carbonTargetVo.setCurrentEmission(FormatUtil.saveTwoDecimalPlace(currentEmission));
            Double targetAmount = carbonTargetVo.getTargetAmount();
            carbonTargetVo.setCurrentPercent(AnalysisUtil.calculateRate(currentEmission, targetAmount));
        }
        carbonTargetVoList = carbonTargetVoList.stream().sorted((o1, o2) -> o2.getCurrentPercent().compareTo(o1.getCurrentPercent())).collect(Collectors.toList());
        return ResponseResult.okResult(carbonTargetVoList);
    }

    @Override
    public ResponseResult queryTargetRank() {
        ResponseResult responseResult = this.queryAllTargetByDate(1);
        List<CarbonTargetVo> targetVoList = (List<CarbonTargetVo>) responseResult.getData();
        targetVoList.stream().sorted((o1, o2) -> o2.getCurrentPercent().compareTo(o1.getCurrentPercent()));
        return ResponseResult.okResult(targetVoList);
    }

    @Override
    public ResponseResult queryTwoHighTarget() {
        ResponseResult responseResult = this.queryAllTargetByDate(1);
        List data = (List) responseResult.getData();
        List<CarbonTargetVo> carbonTargetVoList = new ArrayList<>();
        for (Object o : data) {
            CarbonTargetVo vo = BeanUtil.toBean(o, CarbonTargetVo.class);
            carbonTargetVoList.add(vo);
        }
        carbonTargetVoList = carbonTargetVoList.stream()
                .sorted((o1, o2) -> o2.getCurrentPercent().compareTo(o1.getCurrentPercent()))
                .collect(Collectors.toList());
        if (carbonTargetVoList.size() >= 2) {
            List<CarbonTargetVo> res = new ArrayList<>();
            res.add(carbonTargetVoList.get(0));
            res.add(carbonTargetVoList.get(1));
            return ResponseResult.okResult(res);
        }
        if (carbonTargetVoList.size() == 0) {
            carbonTargetVoList.add(new CarbonTargetVo());
            carbonTargetVoList.add(new CarbonTargetVo());
            return ResponseResult.okResult(carbonTargetVoList);
        }
        carbonTargetVoList.add(new CarbonTargetVo());
        return ResponseResult.okResult(carbonTargetVoList);
    }
}